package com.Hermes.Omega;

import it.unisa.dia.gas.jpbc.*;
import it.unisa.dia.gas.plaf.jpbc.pairing.PairingFactory;
import it.unisa.dia.gas.plaf.jpbc.pairing.a1.TypeA1CurveGenerator;

import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;


public class Omega {

    // private  Pairing pairing;
    private BigInteger q1;//私钥
    private Field Zr;
    private ElementPowPreProcessing gPre;
    private PairingPreProcessing gPair;
    private ElementPowPreProcessing hPre;
    private ElementPowPreProcessing gqPre;
    public static void main(String[] args) throws NoSuchAlgorithmException {
        Omega omega = new Omega();
        PK pk = omega.Gen(256, new SK());//返回公钥产生私钥
        //System.out.println(pk.getN().bitLength());
        // System.out.println(pk.getG().toString());
        Element c = omega.Enc(new BigInteger("1")).getImmutable();
        Element c1 = omega.Enc(new BigInteger("10")).getImmutable();
        Element c2 = omega.Enc(new BigInteger("100")).getImmutable();
        Element c3 = omega.Enc(new BigInteger("1000")).getImmutable();
        Element c4 = omega.Enc(new BigInteger("10000")).getImmutable();
//        Element c5 = omega.Enc(new BigInteger("10")).getImmutable();
        // BigInteger m=omega.Dec(new BigInteger(c), omega.getQ1(),pk);


        long l1 = System.currentTimeMillis();
        BigInteger ans = omega.Dec(c, omega.getQ1());
        long l2 = System.currentTimeMillis();
        BigInteger ans1 = omega.Dec(c1, omega.getQ1());
        long l3 = System.currentTimeMillis();
        BigInteger ans2 = omega.Dec(c2, omega.getQ1());
        long l4 = System.currentTimeMillis();
        BigInteger ans3 = omega.Dec(c3, omega.getQ1());
        long l5 = System.currentTimeMillis();
        BigInteger ans4 = omega.Dec(c4, omega.getQ1());
        long l6 = System.currentTimeMillis();

        System.out.println("10000:(l6-l5) = " + (l6 - l5));
        System.out.println("1000:(l5-l4) = " + (l5 - l4));
        System.out.println("100:(l4-l3) = " + (l4 - l3));
        System.out.println("10：(l3-l2) = " + (l3 - l2));
        System.out.println("1：(l2-l1) = " + (l2 - l1));

        System.out.println("ans = " + ans);
        System.out.println("ans1 = " + ans1);
    }

    public PK Gen(int lambda, SK _sk) {//|SK|=256,|PK|=520;
        SecureRandom rng = new SecureRandom();

        TypeA1CurveGenerator a1 = new TypeA1CurveGenerator(rng, 2, lambda);
        PairingParameters param = a1.generate();
        Pairing pairing =  PairingFactory.getPairing(param);
        Field<Element> G = pairing.getG1();
        Zr = pairing.getZr();
        BigInteger n = param.getBigInteger("n");
        q1 = param.getBigInteger("n0");
        BigInteger q2 = param.getBigInteger("n1");
        Element g = G.newRandomElement().getImmutable();
        gPre=  g.getElementPowPreProcessing();
        gPair =  pairing.getPairingPreProcessingFromElement(g);
        Element u = G.newRandomElement().getImmutable();
        Element h = u.pow(q2).getImmutable();
        hPre=h.getElementPowPreProcessing();
        Element g1 = gPair.pairing(g).getImmutable();
        Element h1 = gPair.pairing(h).getImmutable();
        Field G1 = h.getField();
        _sk.setQ1(q1);
        Element g_q = gPre.pow(q1).getImmutable();
        gqPre=g_q.getElementPowPreProcessing();
        // System.out.println(G1.equals(pairing.getGT()));
        return new PK(n, G, G1, g, h, pairing, h1, g1);

    }

    public Element Enc(BigInteger m) {
        Element r = Zr.newRandomElement().getImmutable();
        return gPre.pow(m).getImmutable().mul(hPre.powZn(r)).getImmutable();

    }

    /*    * 解离散对数问题
     * 使用pollard's lambda解离散对数
     * */
    public BigInteger Dec(Element C, BigInteger _sk) throws NoSuchAlgorithmException {
//        Element g = pk.getG().getImmutable();
        //        ElementPowPreProcessing gPre=g.getElementPowPreProcessing();

        Element P = C.duplicate().pow(_sk).getImmutable();
        //System.out.println(C.duplicate().pow(sk).equals(g.duplicate().pow(q1).pow(new BigInteger("123456"))));
        //true表示加密没问题
        BigInteger res = BigInteger.ZERO;
        while (!P.equals(gqPre.pow(res))) {
            res = res.add(BigInteger.ONE);
        }
        return res;
    }

    //will@Deprecated，密钥应该要通过通信传输
    public BigInteger getQ1() {
        return q1;
    }


}
